package cn.wp.ewallet.wallet.service.impl;

import cn.wp.common.core.authentication.AuthenticationContext;
import cn.wp.common.core.dao.JdbcSupportTemplate;
import cn.wp.common.core.dao.SqlString;
import cn.wp.common.core.dto.WpResult;
import cn.wp.common.core.id.Id;
import cn.wp.common.core.util.BeanUtil;
import cn.wp.ewallet.wallet.dao.*;
import cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant;
import cn.wp.ewallet.wallet.extension.util.EwalletWalletUtil;
import cn.wp.ewallet.wallet.model.dto.AccountInfoDto;
import cn.wp.ewallet.wallet.model.dto.AccountLogDto;
import cn.wp.ewallet.wallet.model.dto.MoneyChangeParamDto;
import cn.wp.ewallet.wallet.model.dto.UserWalletRelDto;
import cn.wp.ewallet.wallet.model.entity.*;
import cn.wp.ewallet.wallet.service.AccountInfoService;
import cn.wp.ewallet.wallet.service.UserWalletRelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import static cn.wp.ewallet.wallet.extension.constant.EwalletWalletConstant.*;

/**
 * @ClassName AccountInfoServiceImpl
 * @Author ruixule
 * @Date 2020/1/7 10:52
 */
@Service
public class AccountInfoServiceImpl extends BaseServiceImpl<AccountInfoDo> implements AccountInfoService {
    @Autowired
    private AccountInfoDao accountInfoDao;

    //账户变动记录
    @Autowired
    private AccountLogDao accountLogDao;
    //转账记录
    @Autowired
    private TransAccountLogDao transAccountLogDao;
    @Autowired
    private RechargeShqcLogDao rechargeShqcLogDao;

    @Autowired
    private UserWalletRelService userWalletRelService;

    @Autowired
    private AuthenticationContext authenticationContext;

    @Autowired
    private JdbcSupportTemplate jdbcSupportTemplate;

    public AccountInfoServiceImpl(JdbcSupportTemplate jdbcSupportTemplate) {
        super(jdbcSupportTemplate);
    }

    /**
     * 查询账户 信息（余额）
     *
     * @param walletAddress
     * @param moneyTypeId
     * @return
     */
    @Override
    public AccountInfoDto selectAccountByWalletAddMoneyTypeId(String walletAddress, String moneyTypeId, String accountType) {
        AccountInfoDoo accountInfoDoo = accountInfoDao.selectAccountByWalletAddMoneyTypeId(walletAddress, moneyTypeId, accountType);
        return BeanUtil.copyProperties(accountInfoDoo, AccountInfoDto.class);
    }

    /**
     * SHQC转账逻辑
     *
     * @param transferAccountParamDto
     * @return
     */
    @Transactional
    @Override
    public synchronized WpResult transferMoney(MoneyChangeParamDto transferAccountParamDto) {
        //转入地址校验
        if (!EwalletWalletUtil.isETHValidAddress(transferAccountParamDto.getToWalletAddress())) {
            return new WpResult().failure(CHECK_ADDRESS_FAILED_CODE, "地址错误");
        }
        AccountInfoDoo accountInfoDoo = accountInfoDao.selectAccountByWalletAddMoneyTypeId(transferAccountParamDto.getToWalletAddress(), transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);
        if (null == accountInfoDoo) {
            return new WpResult().failure(CHECK_ADDRESS_NOT_EXIST_CODE, "转入地址不存在");
        }
        //查询当前用户信息
        UserWalletRelDto userWalletRelDto = userWalletRelService.getUserWalletRel();
        String operUser = authenticationContext.currentUser().getId();

        //获取转账方的信息
        AccountInfoDoo fromAccountInfoDoo = accountInfoDao.selectAccountByWalletAddMoneyTypeId(userWalletRelDto.getWalletAddress(), transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);
        //币种数据精度
        String monyTypeUnit = fromAccountInfoDoo.getMoneyTypeUnit();
        //转账金额 * 100000
        BigDecimal money = EwalletWalletUtil.toLowerWei(transferAccountParamDto.getMoney(), new BigDecimal(monyTypeUnit));
        //手续费
        BigDecimal serviceCharge = EwalletWalletUtil.toLowerWei(transferAccountParamDto.getServiceCharge(), new BigDecimal(monyTypeUnit));
        //转账总计
        BigDecimal totalAmount = money.add(serviceCharge);
        //判断账户余额是否足够
        if ((fromAccountInfoDoo.getMoneyAmount().subtract(totalAmount)).compareTo(BigDecimal.valueOf(0)) == -1) {
            return new WpResult().failure(CHECK_AMOUNT_FAILED_CODE, "账户余额不足");
        } else {
            //转出方账户资金变动保存
            saveAccountChange(fromAccountInfoDoo.getWalletAddress(), totalAmount, ACCOUNT_SUBTRACT, ACCOUNT_CHARGE_CASE_TRANSFER, transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);

            //转入方账户资金变动保存
            saveAccountChange(transferAccountParamDto.getToWalletAddress(), money, ACCOUNT_ADD, ACCOUNT_CHARGE_CASE_TRANSFER, transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);

            //封装转账记录
            TransAccountLogDo transAccountLogDo = new TransAccountLogDo(Id.gen(), userWalletRelDto.getWalletAddress(), transferAccountParamDto.getToWalletAddress(), new Date()
                    , money, serviceCharge, totalAmount, TRANSFER_OPER_RESULT_SUCCESS, operUser, transferAccountParamDto.getRemark(), transferAccountParamDto.getMoneyTypeId());
            transAccountLogDo.setOperType(TRANS_LOG_TYPE_SYSTEM);//系统内部转账
            //保存转账记录日志
            transAccountLogDao.insertTransAccountLogDo(transAccountLogDo);
            return new WpResult().success(true);
        }
    }

    /**
     * 充值逻辑
     *
     * @param transferAccountParamDto
     * @return 充值无手续费
     */
    @Transactional
    @Override
    public synchronized WpResult rechargeMoney(MoneyChangeParamDto transferAccountParamDto) {

        //获取充值账户的信息
        AccountInfoDoo accountInfoDoTo = accountInfoDao.selectAccountByWalletAddMoneyTypeId(transferAccountParamDto.getToWalletAddress(), transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);
        if (null == accountInfoDoTo) {
            return new WpResult().failure(CHECK_ADDRESS_NOT_EXIST_CODE, "充值地址不存在");
        }

        //更新充值账户资产变动信息
        BigDecimal money = EwalletWalletUtil.toLowerWei(transferAccountParamDto.getMoney(), new BigDecimal(accountInfoDoTo.getMoneyTypeUnit()));
        saveAccountChange(transferAccountParamDto.getToWalletAddress(), money, ACCOUNT_ADD, ACCOUNT_CHARGE_CASE_RECHARGE, transferAccountParamDto.getMoneyTypeId(), ACCOUNT_TYPE_AVAILABLE);

        //保存充值记录
        RechargeShqcLogDo rechargeShqcLogDo = new RechargeShqcLogDo(Id.gen(), transferAccountParamDto.getToWalletAddress(), transferAccountParamDto.getCurrencyType(), money, authenticationContext.currentUser().getId(), new Date());
        rechargeShqcLogDao.saveRechargeShqcLog(rechargeShqcLogDo);
        return new WpResult().success(true);
    }


    @Override
    public void saveAccountInfoDo(String type, String accountName, String address, String accoutType) {
        AccountInfoDo accountInfoDo = new AccountInfoDo();
        accountInfoDo.setAccountId(Id.gen());
        accountInfoDo.setWalletAddress(address);
        accountInfoDo.setMoneyTypeId(type);
        accountInfoDo.setAccoutType(accoutType);
        accountInfoDo.setAccountName(accountName);
        accountInfoDo.setMoneyAmount(BigDecimal.valueOf(0));//默认账户0
        accountInfoDao.insertAccountInfo(accountInfoDo);
    }

    @Override
    public BigDecimal selectFinaMoneyAmount(String address, String moneyTypeId) {
        return accountInfoDao.selectFinaMoneyAmount(address, moneyTypeId);
    }


    /**
     * 通过账号id 获取账户的余额信息
     *
     * @param
     * @return
     */
    @Override
    public AccountInfoDo getAccountInfoDoByAccountId(String accountId) {
        return accountInfoDao.selectAccountInfoByAccountId(accountId);
    }

    @Override
    public void transferAccount(BigDecimal amount, String fromAccountId, String toAccountId, String moneyTypeId) {
        AccountInfoDo fromAccountInfoDo = accountInfoDao.selectAccountInfoByAccountId(fromAccountId, moneyTypeId);
        BigDecimal beforeFromAccount = fromAccountInfoDo.getMoneyAmount();


        BigDecimal afterFromAccount = beforeFromAccount.subtract(amount);
        if (afterFromAccount.doubleValue() < 0) {
            return;
        }
        accountInfoDao.updateAccount(afterFromAccount, fromAccountId, moneyTypeId);
        AccountInfoDo toAccountInfoDo = accountInfoDao.selectAccountInfoByAccountId(toAccountId, moneyTypeId);
        BigDecimal beforeToAccount = toAccountInfoDo.getMoneyAmount();
        BigDecimal afterToAccount = beforeToAccount.add(amount);
        accountInfoDao.updateAccount(afterToAccount, toAccountId, moneyTypeId);
        AccountLogDo fromAccountLogDo = new AccountLogDo(Id.gen(), fromAccountId, beforeFromAccount, afterFromAccount, new Date(), "1000", amount);
        accountLogDao.insertAccountLog(fromAccountLogDo);
        AccountLogDo toAccountLogDo = new AccountLogDo(Id.gen(), toAccountId, beforeToAccount, afterToAccount, new Date(), "2000", amount);
        accountLogDao.insertAccountLog(toAccountLogDo);
    }

    @Override
    public String getAddressByAccountId(String accountId) {
        return accountInfoDao.selectAddressByAccountId(accountId);
    }

    @Transactional
    @Override
    public void increaseMoneyAmount(String accountId, BigDecimal dailyEarn) {
        accountInfoDao.increaseMoneyAmount(accountId, dailyEarn);
    }

    @Override
    public void exchange(String accountId, BigDecimal amount) {
//        AccountLogDo
        AccountInfoDo accountInfoDo = select("account_Id = '" + accountId + "'" +
                " AND ACCOUT_TYPE='1000' AND MONEY_TYPE_ID='2000'").get(0);
        this.execute(new StringBuilder().append("update account_info ")
                .append("set MONEY_AMOUNT = '")
                .append(accountInfoDo.getMoneyAmount().add(amount))
                .append("'")
                .append(" where account_Id = '")
                .append(accountId).append("'")
                .append(" AND ACCOUT_TYPE='1000' AND MONEY_TYPE_ID='2000'").toString());

    }

    @Override
    public String shqcAccountId(String userId) {
        SqlString sqlString = new SqlString();
        sqlString.add("select a.*");
        sqlString.add(" from account_info a");
        sqlString.add("    left join user_wallet_rel w on a.WALLET_ADDRESS = w.WALLET_ADDRESS");
        sqlString.add("    where w.USER_ID = '?'");
        sqlString.add("   and a.ACCOUT_TYPE='1000' and a.MONEY_TYPE_ID='2000'");
        Object[] objects = new Object[]{
                userId,
        };
        AccountInfoDoo ado = jdbcSupportTemplate.selectObject(sqlString.build(), objects, AccountInfoDoo.class);
        return ado.getAccountId();
    }

    @Override
    public AccountInfoDo selectAccountByWalletAddAccountType(String walletAddress, String accountType) {
        return accountInfoDao.selectAccountByWalletAddAccountType(walletAddress, accountType);
    }

    @Override
    public void update(AccountInfoDto accountInfoDto) {
        accountInfoDao.update(BeanUtil.copyProperties(accountInfoDto, AccountInfoDo.class));

    }

    @Override
    public void modifyAccountInfoById(AccountInfoDto accountInfoDto) {
        accountInfoDao.updateAccountInfoById(BeanUtil.copyProperties(accountInfoDto, AccountInfoDo.class));
    }

    /**
     * 可用账户资产变动并保存资产变动信息
     *
     * @param address     变动地址
     * @param tokenBlance 变动金额
     * @param symbol      操作符号，1=正数，-1=负数
     * @param changeCause 变动原因
     * @param moneyTypeId 操作币种
     * @param accountType 账户类型
     */
    @Override
    public void saveAccountChange(String address, BigDecimal tokenBlance, int symbol, String changeCause, String moneyTypeId, String accountType) {

        //查询账户资产信息
        AccountInfoDto accountInfoDto = selectAccountByWalletAddMoneyTypeId(address, moneyTypeId, accountType);
        if (null == accountInfoDto) {
            return;
        }
        //更新USDT账户资产
        AccountInfoDto updateDto = new AccountInfoDto();
        updateDto.setAccountId(accountInfoDto.getAccountId());
        if (ACCOUNT_ADD == symbol) {
            updateDto.setMoneyAmount(tokenBlance);
        } else {
            updateDto.setMoneyAmount(tokenBlance.negate());
        }

        modifyAccountInfoById(updateDto);
        //修改资产变动信息
        AccountLogDo accountLogDo = new AccountLogDo();
        accountLogDo.setLogId(Id.gen());
        accountLogDo.setAccountId(accountInfoDto.getAccountId());
        if (ACCOUNT_ADD == symbol) {
            accountLogDo.setBalanceAmount(tokenBlance);
        } else {
            accountLogDo.setBalanceAmount(tokenBlance.negate());
        }

        accountLogDo.setChangeCause(changeCause);
        accountLogDao.insertAccountLog(accountLogDo);
    }

}
